﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace PRWPF
{
    public delegate FileInfo[] FileFilterDelegate(FileInfo[] Files);

    public delegate void FileCollectedDelegate(int FileCountInStep);

    public delegate void FileProcessResultDelegate(string FileName, FileProcessResult Result);

    public delegate void DebugEventDelegate(string DebugMessage);

    public enum FileProcessResult
    {
        HashBuildSucceed,
        FileReadFail,
        DuplicateFound
    }

    public static class FolderFileHelper
    {
        public static event DebugEventDelegate DebugOutput;

        private static bool thingsCanGoOn = false;
        private static object olock = new object();

        public static bool ThingsCanGoOn
        {
            get
            {
                lock (olock)
                {
                    return thingsCanGoOn;
                }
            }
            set
            {
                lock (olock)
                {
                    thingsCanGoOn = value;
                }
            }
        }

        public static Dictionary<string, List<FileInfo>> GetFileDigits(
            List<FileInfo> Files, FileProcessResultDelegate FileProcessHandler, bool IsSimple)
        {
            MD5 md5 = MD5.Create();
            var ret = new Dictionary<string, List<FileInfo>>();
            int hashTotalCount;
            if (IsSimple)
            {
                hashTotalCount = (int)(Properties.Settings.Default.SimpleDigitDataqLimitMB * 1024 * 1024);
            }
            else
            {
                hashTotalCount = (int)(Properties.Settings.Default.DigitDataLimitMB * 1024 * 1024);
            }
            int hashBlockCount = hashTotalCount / Properties.Settings.Default.DigitDataSplitCount;
            hashTotalCount = (hashBlockCount + 2) * Properties.Settings.Default.DigitDataSplitCount;

            foreach (var f in Files)
            {
                if (!ThingsCanGoOn)
                {
                    break;
                }
                if (f.Exists)
                {
                    string fk = null;
                    try
                    {
                        if (f.Length < hashTotalCount)
                        {
                            using (FileStream fs = f.OpenRead())
                            {
                                fk = Convert.ToBase64String(md5.ComputeHash(fs));
                            }
                        }
                        else
                        {
                            using (FileStream fs = f.OpenRead())
                            {
                                byte[] block = new byte[hashTotalCount];
                                int fileBlockLength = (int)(f.Length / Properties.Settings.Default.DigitDataSplitCount);
                                for (int i = 0; i < Properties.Settings.Default.DigitDataSplitCount; i++)
                                {
                                    fs.Seek(i * (long)fileBlockLength, SeekOrigin.Begin);
                                    fs.Read(block, i * hashBlockCount, hashBlockCount);
                                }
                                fk = Convert.ToBase64String(md5.ComputeHash(block));
                            }
                        }
                        FileProcessHandler(f.FullName, FileProcessResult.HashBuildSucceed);
                    }
                    catch (Exception ex)
                    {
                        FileProcessHandler(f.FullName, FileProcessResult.FileReadFail);
                        OutputMessage("创建文件摘要失败：" + f.FullName + " " + ex.Message);
                    }
                    if (fk != null)
                    {
                        if (!ret.ContainsKey(fk))
                        {
                            ret[fk] = new List<FileInfo>();
                        }
                        else
                        {
                            FileProcessHandler(f.FullName, FileProcessResult.DuplicateFound);
                            OutputMessage("重复的文件：\r\n" + ret[fk][0].FullName + "\r\n" + f.FullName);
                        }
                        ret[fk].Add(f);
                    }
                }
                else
                {
                    OutputMessage("找不到文件，忽略：" + f.FullName);
                }
            }
            return ret;
        }

        public static List<FileInfo> GetDirectoryFiles(
            string DirectoryName, FileFilterDelegate FileFilter, FileCollectedDelegate FileCollectStepHandler)
        {
            List<FileInfo> ret = new List<FileInfo>();
            if (Directory.Exists(DirectoryName))
            {
                var di = new DirectoryInfo(DirectoryName);
                try
                {
                    var fis = di.GetFiles();
                    if (FileFilter != null)
                    {
                        fis = FileFilter(fis);
                    }
                    ret.AddRange(fis);
                    FileCollectStepHandler(fis.Length);
                }
                catch (Exception ex)
                {
                    OutputMessage("获取目录内文件失败：" + di.FullName + " " + ex.Message);
                }

                try
                {
                    foreach (var item in di.GetDirectories())
                    {
                        if (!ThingsCanGoOn)
                        {
                            break;
                        }
                        ret.AddRange(GetDirectoryFiles(item.FullName, FileFilter, FileCollectStepHandler));
                    }
                }
                catch (Exception ex)
                {
                    OutputMessage("获取子目录失败：" + di.FullName + " " + ex.Message);
                }
            }
            else
            {
                string msg = "找不到指定的目录，可能已经被移除或更名，已跳过：" + DirectoryName;
                OutputMessage(msg);
            }
            return ret;
        }

        public static bool ReorganizeFile(
            FileInfo SourceFile, DirectoryInfo DestDir, string DestFileName, bool KeepSource)
        {
            string cadFileName = DestFileName;
            int fileRenameIndex = 0;
            int extIndex = DestFileName.LastIndexOf('.');
            while (File.Exists(CombinePaths(DestDir.FullName, DestFileName)))
            {
                if (extIndex != -1)
                {
                    cadFileName = DestFileName.Insert(extIndex, "_" + fileRenameIndex.ToString());
                }
                else
                {
                    cadFileName = DestFileName + "_" + fileRenameIndex.ToString();
                }
                fileRenameIndex++;
            }
            if (cadFileName != DestFileName)
            {
                OutputMessage("文件已自动更名：" + DestFileName + "->" + cadFileName);
            }
            try
            {
                if (KeepSource)
                {
                    SourceFile.CopyTo(CombinePaths(DestDir.FullName, cadFileName));
                }
                else
                {
                    SourceFile.MoveTo(CombinePaths(DestDir.FullName, cadFileName));
                }
                return true;
            }
            catch (Exception ex)
            {
                string msg = "复制或移动文件失败：" + ex.Message;
                OutputMessage(msg);
                return false;
            }
        }

        private static DateTime? GetFileTime(FileInfo File)
        {
            if (File.Exists)
            {
                return (new DateTime[] { File.CreationTime, File.LastAccessTime, File.LastWriteTime }).Min();
            }
            else
            {
                string msg = "找不到指定的文件，可能已经被移除或更名，已跳过：" + File.FullName;
                return null;
            }
        }

        private static void OutputMessage(string Msg)
        {
            if (DebugOutput != null)
            {
                DebugOutput(Msg);
            }
            else
            {
                System.Diagnostics.Debug.WriteLine(Msg);
            }
        }

        public static string CombinePaths(params string[] Paths)
        {
            string ret = string.Empty;
            foreach (var item in Paths)
            {
                string p = item;
                if (p.First() == Path.DirectorySeparatorChar)
                {
                    p = p.Remove(0, 1);
                }
                if (p.Last() == Path.DirectorySeparatorChar)
                {
                    p = p.Remove(p.Length - 1, 1);
                }
                ret += p + Path.DirectorySeparatorChar;
            }
            return ret.Remove(ret.Length - 1, 1);
        }

        public static ImageMetadata GetImageMetadata(FileInfo File)
        {
            using (FileStream fs = File.OpenRead())
            {
                BitmapFrame bf = BitmapFrame.Create(fs, BitmapCreateOptions.None, BitmapCacheOption.OnLoad);
                return bf.Metadata;
            }
        }
    }
}